package com.huangfu.learning.javabase.java.pattern.structure.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * Created with IntelliJ IDEA.
 * Author: huangfu.daohui
 * Date: 2018/7/20
 * Time: 15:22
 * Version: 1.0
 * Description:JDK动态代理练习
 * 代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能用动态代理
 **/
public class DynamicProxyPractice {
    //测试
    public static void main(String[] args) {
        //目标类
        IUserDaoDynamic target = new TargetUserDaoDynamic();
        // 【原始的类型 class cn.itcast.b_dynamic.UserDao】
        System.out.println(target.getClass());
        // 给目标对象，创建代理对象
        IUserDaoDynamic proxy = (IUserDaoDynamic) new ProxyFactoryClass(target).getProxyInstence();

        // 执行方法   【代理对象】
        proxy.save();
        proxy.query("huangfu");
    }
}

//接口
interface IUserDaoDynamic {
    void save();

    String query(String name);
}

//目标类
class TargetUserDaoDynamic implements IUserDaoDynamic {

    @Override
    public void save() {
        System.out.println("动态代理目标类UserDaoDynamic.save()保存成功。。。。");
    }

    @Override
    public String query(String name) {
        return "动态代理目标类UserDaoDynamic.query()查询成功。。。。";
    }
}

/**
 * 创建动态代理对象
 * 动态代理不需要实现接口,但是需要指定接口类型
 * 这一个可以代理所有的目标类
 */
class ProxyFactoryClass {

    //维护一个目标类
    private Object target;

    public ProxyFactoryClass(Object target) {
        this.target = target;
    }

    //给目标对象生成代理对象
    public Object getProxyInstence() {

        //三个参数
        //1、ClassLoader loader：指定当前目标对象使用类加载器,获取加载器的方法是固定的
        //2、Class<?>[] interfaces：标对象实现的接口的类型,使用泛型方式确认类型
        //3、InvocationHandler h：事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                System.out.println("invoke（开始事务、权限等）...");
                //执行目标对象方法
                Object returnValue = method.invoke(target, args);
                System.out.println("invoke之后的操作（提交事务、日志等）...");
                return returnValue;
            }
        });
    }
}